/*
 * Copyright (C) 2010 Peter Schönhofen
 *
 * This file is part of the Akkad Compiler Framework.
 *
 * The Akkad Compiler Framework is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

package hu.anaxo.akkad.vm

import collection.mutable.{HashMap, Map, Stack}

/**
 * Virtual machine
 */

object Executor {

  /**
   * Execute program
   *
   * @param program program
   */

  def execute(program: List[Instruction]): Unit = {

    // preparations

    var data: Stack[Stack[Map[Int, Int]]] = new Stack
    val stack: Stack[Int] = new Stack
    var current = 0

    // execute program

    while (current != -1) {
      program(current) match {
        case CN(value) =>
          stack.push(value)
        case GV(level, address) =>
          stack.push(data(data.length - 1 - level).top(address))
        case SV(level, address) =>
          data(data.length - 1 - level).top(address) = stack.pop
        case JP(label) =>
          current = label.get - 1
        case CL(label) =>
          stack.push(current)
          current = label.get - 1
        case JT(label) =>
          if (stack.pop == 1)
            current = label.get - 1
        case JF(label) =>
          if (stack.pop == 0)
            current = label.get - 1
        case EN(level) =>
          if (data.length == level)
            data.push(new Stack)
          data(data.length - 1 - level).push(new HashMap)
        case RT(level) =>
          data(data.length - 1 - level).pop
          if (data(data.length - 1 - level).isEmpty)
            data.pop
          current = if (!stack.isEmpty) stack.pop else -2
        case NG() =>
          stack.push(-stack.pop)
        case AD() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(left + right)
        case SB() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(left - right)
        case ML() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(left * right)
        case DV() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(left / right)
        case OD() =>
          stack.push(if (stack.pop % 2 == 1) 1 else 0)
        case EQ() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(if (left == right) 1 else 0)
        case LT() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(if (left < right) 1 else 0)
        case GT() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(if (left > right) 1 else 0)
        case NE() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(if (left != right) 1 else 0)
        case LE() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(if (left <= right) 1 else 0)
        case GE() =>
          val right = stack.pop
          val left = stack.pop
          stack.push(if (left >= right) 1 else 0)
        case PR() =>
          println(stack.pop)
      }
      current += 1
    }
  }
}